package com.uum.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.uum.blog.pojo.entity.Blog;
import com.uum.blog.mapper.BlogMapper;
import com.uum.blog.pojo.entity.BlogTag;
import com.uum.blog.pojo.form.BlogForm;
import com.uum.blog.pojo.query.BlogPageQuery;
import com.uum.blog.pojo.vo.BlogVO;
import com.uum.blog.service.BlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.uum.blog.service.BlogTagService;
import com.uum.blog.service.EsBlogService;
import com.uum.blog.util.MarkdownUtils;
import com.uum.mq.api.MqFeignClient;
import com.uum.web.exception.BizException;
import com.uum.web.util.JwtUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.*;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements BlogService {

    private final EsBlogService esBlogService;
    private final BlogTagService blogTagService;
    private final MqFeignClient mqFeignClient;

    @Override
    public IPage page(BlogPageQuery pageQuery) {
        IPage page = new Page(pageQuery.getCurrentPage(), pageQuery.getPageSize());
        try {
//            int i = 10 / 0;
            Map<String, Object> blogPage = esBlogService.blogPage(pageQuery);
            page.setRecords((List) blogPage.get("source"));
            page.setTotal((Long) blogPage.get("total"));
            return page;
        } catch (Exception e) {
            log.error("es异常==》" + e.getMessage());
            //es发生异常时查询数据库获取数据
            return pageFromDB(pageQuery);
        }
    }


    /**
     * 分页查询博文
     *
     * @param pageQuery
     * @return
     */
    private IPage<BlogVO> pageFromDB(BlogPageQuery pageQuery) {
        int currentPage = pageQuery.getCurrentPage();
        int pageSize = pageQuery.getPageSize();
        IPage<BlogVO> page = new Page<>(currentPage, pageSize);
        //condition
        QueryWrapper<BlogVO> queryWrapper = new QueryWrapper<BlogVO>()
                .eq("t1.is_deleted", 0)
                .eq(!JwtUtils.isAdministrator(), "t1.user_id", JwtUtils.getUserId())
                .eq(pageQuery.getIsRelease() != null, "t1.is_release", pageQuery.getIsRelease())
                .eq(pageQuery.getCategoryId() != null, "t1.category_id", pageQuery.getCategoryId())
                .eq(pageQuery.getIsComment() != null, "t1.is_comment", pageQuery.getIsComment())
                .eq(pageQuery.getIsTop() != null, "t1.is_top", pageQuery.getIsTop())
                .like(StrUtil.isNotBlank(pageQuery.getTitle()), "t1.title", pageQuery.getTitle())
//                        .in(StrUtil.isNotBlank(pageQuery.getTagIds()), "t3.tag_id", pageQuery.getTagIds() == null ? null : Arrays.asList(pageQuery.getTagIds().split(",")))
                .between(StrUtil.isNotBlank(pageQuery.getStartTime()) && StrUtil.isNotBlank(pageQuery.getEndTime()), "t1.create_time", pageQuery.getStartTime(), pageQuery.getEndTime())
                .orderByDesc("t1.create_time");

        //search
        List<BlogVO> pageList = baseMapper.page(queryWrapper);

        //filter tagIds
        List<Long> tagIds = pageQuery.getTagIds();
        if (CollUtil.isNotEmpty(tagIds)) {
            pageList = pageList.stream().filter(item -> item.getTagIds().containsAll(tagIds)).collect(Collectors.toList());
        }

        //page
        List<BlogVO> pageForBlog = CollUtil.page(currentPage - 1, pageSize, pageList);
        page.setTotal(pageList.size());
        page.setRecords(pageForBlog);
        return page;
    }

    @Override
    public List<Map<String, String>> listSuggest(String title) {
        List<Map<String, String>> suggestList = esBlogService.listSuggest(title);
        return suggestList;
    }

    @Override
    @Transactional
    public Boolean updateAuthorName(Long userId, String nickname) {
        boolean es = false;
        //update DB
        boolean db = this.update(new LambdaUpdateWrapper<Blog>().set(Blog::getAuthor, nickname).eq(Blog::getUserId, userId));
        //update ES
        if (db) {
            es = esBlogService.updateAuthorName(userId, nickname);
        }
        return es && db;
    }


    @Override
    @Transactional
    public boolean settingBlog(Blog blog) {
        Blog original = baseMapper.selectById(blog.getId());
        //置顶判断
        if (blog.getIsTop() != original.getIsTop()) {
            //将原先置顶的取消
            this.update(new LambdaUpdateWrapper<Blog>().set(Blog::getIsTop, 0).eq(Blog::getIsTop, 1));
        }
        //update DB
        int db = baseMapper.updateById(blog);
        if (db > 0) {
            blog.setTitle(original.getTitle());
            //update ES
            boolean es = esBlogService.settingBlog(blog);
            Integer isRelease = blog.getIsRelease();
            if (es && (original.getIsRelease() != isRelease) && isRelease == 1) {
                //send to rabbitmq
                mqFeignClient.sendBlog(blog.getId(), blog.getTitle());
            }
        }
        return db > 0;
    }

    @Override
    public BlogVO getTop() {
        return baseMapper.getTop();
    }


    @Override
    public BlogVO detail(Long id) {
        return baseMapper.detail(id);
    }


    @Override
    @Transactional
    public Boolean addBlog(BlogForm blogForm) {
        blogForm.setAuthor(JwtUtils.getNickname());
        blogForm.setUserId(JwtUtils.getUserId());
        Blog blog = new Blog();
        BeanUtil.copyProperties(blogForm, blog, "createTime", "modifiedTime");
        //保存到数据库
        int insert = baseMapper.insert(blog);
        if (insert > 0) {
            //TODO 使用线程串行优化代码
            blogForm.setId(blog.getId());
            blogForm.setCreateTime(DateUtil.formatLocalDateTime(blog.getCreateTime()));
            blogForm.setClickNum(0);
            blogForm.setCommentCount(0);
            //已发布 insert es suggest
            if (blogForm.getIsRelease() == 1) {
                try {
                    //insert into es index blog-search-suggest
                    esBlogService.insertSuggest(blogForm.getId().toString(), blogForm.getTitle());
                    //send to rabbitmq
                    mqFeignClient.sendBlog(blog.getId(), blog.getTitle());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            //insert into es index blog-info
            esBlogService.add(blogForm);

            //insert into mysql
            List<Long> tagIds = blogForm.getTagIds();
            if (!tagIds.isEmpty()) {
                insertBlogTag(blogForm.getId(), tagIds);
            }

        }
        return insert > 0;
    }

    @Override
    @Transactional
    public Boolean updateBlog(BlogForm blogForm) {
        List<Long> oldTagIds = blogTagService.list(new LambdaQueryWrapper<BlogTag>().eq(BlogTag::getBlogId, blogForm.getId()).select(BlogTag::getTagId))
                .stream().map(BlogTag::getTagId).collect(Collectors.toList());
        Blog blog = new Blog();
        BeanUtil.copyProperties(blogForm, blog, "createTime", "modifiedTime");
        int update = baseMapper.updateById(blog);
        if (update > 0) {
            //update es data
            blogForm.setModifiedTime(DateUtil.formatLocalDateTime(blog.getModifiedTime()));
            esBlogService.update(blogForm, true);

            //update mysql data
            List<Long> newTagIds = blogForm.getTagIds();
            //update mysql tag data
            if (!(oldTagIds.size() == newTagIds.size() && oldTagIds.containsAll(newTagIds))) {
                blogTagService.remove(new LambdaQueryWrapper<BlogTag>().eq(BlogTag::getBlogId, blog.getId()));
                insertBlogTag(blog.getId(), newTagIds);
            }
        }
        return update > 0;
    }

    @Override
    @Transactional
    public Boolean delete(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        int result = baseMapper.deleteBatchIds(idList);
        if (result > 0) {
            //delete tag
            blogTagService.remove(new LambdaQueryWrapper<BlogTag>().in(BlogTag::getBlogId, idList));
            //delete from es
            esBlogService.delete(idList);
        }
        return result > 0;

    }

    @Override
    public String uploadMarkdownFile(MultipartFile fileData) {
        String blogContent = null;
        if (fileData == null) {
            throw new BizException("请选中需要上传的文件");
        }
        String fileOriginalName = fileData.getOriginalFilename();
        if (MarkdownUtils.isMarkdown(fileOriginalName)) {
            try {
                Reader reader = new InputStreamReader(fileData.getInputStream(), "utf-8");
                BufferedReader br = new BufferedReader(reader);
                String line;
                String content = "";
                while ((line = br.readLine()) != null) {
                    content += line + "\n";
                }
                blogContent = content;
            } catch (Exception e) {
                log.error("文件解析出错");
                log.error(e.getMessage());
            }
        } else {
            throw new BizException("暂时只支持MarkDown文件");
        }
        return blogContent;
    }

    private void insertBlogTag(Long blogId, List<Long> tagIds) {
        if (!tagIds.isEmpty()) {
            List<BlogTag> blogTagList = new ArrayList<>();
            for (Long tagId : tagIds) {
                BlogTag blogTag = new BlogTag();
                blogTag.setBlogId(blogId);
                blogTag.setTagId(tagId);
                blogTagList.add(blogTag);
            }
            blogTagService.saveBatch(blogTagList);
        }
    }

    @Override
    public Map<String, Object> listBlogContributeCount() {
        //当前时间
        DateTime startTime = new DateTime();
        //一年前
        DateTime endTime = DateUtil.offsetDay(startTime, -365);
        //创建一年内的日期范围
        DateRange range = DateUtil.range(endTime, startTime, DateField.DAY_OF_YEAR);
        List<String> dateList = new ArrayList<>();
        while (range.hasNext()) {
            dateList.add(DateUtil.formatDate(range.next()));
        }
        //格式化时间
        String startTimeStr = DateUtil.formatDateTime(startTime);
        String endTimeStr = DateUtil.formatDateTime(endTime);
        //查询该时间段内已发布且未删除的博文
        List<Map<String, Object>> blogContributeMap = baseMapper.listBlogContributeCount(startTimeStr, endTimeStr); //[{DATE=2020-07-13, COUNT=2}, {DATE=2020-07-16, COUNT=1}, {DATE=2020-08-08, COUNT=3}]

        //组装数据
        Map<String, Object> dateMap = new HashMap<>();
        for (Map<String, Object> itemMap : blogContributeMap) {
            dateMap.put(itemMap.get("DATE").toString(), itemMap.get("COUNT"));
        }

        List<List<Object>> resultList = new ArrayList<>();
        for (String item : dateList) {
            Integer count = 0;
            if (dateMap.get(item) != null) {
                count = Integer.valueOf(dateMap.get(item).toString());
            }
            List<Object> objectList = new ArrayList<>();
            objectList.add(item);
            objectList.add(count);
            resultList.add(objectList);
        }

        Map<String, Object> resultMap = new HashMap<>();
        List<String> contributeDateList = new ArrayList<>();
        contributeDateList.add(startTimeStr);
        contributeDateList.add(endTimeStr);
        resultMap.put("contributeDate", contributeDateList);
        resultMap.put("blogContributeCount", resultList);

        return resultMap;
    }

    @Override
    public Map<String, Object> listCategoryPieData() {
        List<Map<String, Object>> data = baseMapper.listCategoryPieData();
        return getMap(data);
    }

    @Override
    public Map<String, Object> listTagPieDate() {
        List<Map<String, Object>> data = baseMapper.listTagPieDate();
        return getMap(data);
    }


    private Map getMap(List<Map<String, Object>> data) {
        List valueList = new ArrayList();
        for (int i = 0; i < data.size(); i++) {
            valueList.add(data.get(i).get("name"));
        }
        Map map = new HashMap();
        map.put("value", valueList);
        map.put("name", data);
        return map;
    }


}
